package com.tansun.easycare.core.filter;

import java.io.IOException;
import java.util.Enumeration;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.tansun.easycare.modules.sys.entity.XssHttpServletRequestWrapper;
import com.tansun.rule.utils.StringUtils;

/**
 * 过滤非法http参数信息
 */
public class IllegalHttpParamFilter implements Filter {

    private FilterConfig filterConfig;
    Logger logger = LoggerFactory.getLogger(IllegalHttpParamFilter.class);
    private static final String LEGAL_URI_WITH_PARAMNAMES_SEPERATOR = "#";
    private static final String COMMA_SEPERATOR = ",";

    /**
     * 在配置中指定的uri对应参数不被过滤
     * */
    private String[] legalUriParamNameArr;
    /**
     * 过滤的非法字符集合
     * */
    private String[] invalidParamArr;
    /**
     * 需要进行转义参数的路径
     * */
    String[] encodeFiltPathArr;
    /**
     * 字符编码
     * */
    private String charset;

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        this.filterConfig = filterConfig;
        String legalUriWithParamNames = filterConfig.getInitParameter("legalUriWithParamNames");
        String invalidParams = filterConfig.getInitParameter("invalidParams");
        String encodeFiltPath = filterConfig.getInitParameter("encodeFiltPath");
        String initCharset = filterConfig.getInitParameter("charset");
        if (!StringUtils.isEmpty(invalidParams)) {
            invalidParamArr = invalidParams.split(COMMA_SEPERATOR);
            trimArr(invalidParamArr);
        }

        if (!StringUtils.isEmpty(legalUriWithParamNames)) {
            legalUriParamNameArr = legalUriWithParamNames.split(COMMA_SEPERATOR);
            trimArr(legalUriParamNameArr);
        }

        if (!StringUtils.isEmpty(encodeFiltPath)) {
            encodeFiltPathArr = encodeFiltPath.split(COMMA_SEPERATOR);
            trimArr(encodeFiltPathArr);
        }

        if (StringUtils.isEmpty(initCharset)) {
            initCharset = "UTF-8";
        }

        charset = initCharset;

    }

    private void trimArr(String[] arr) {
        if (arr == null || arr.length == 0) {
            return;
        }

        for (int i = 0; i < arr.length; i++) {
            String str = arr[i];
            str = str.replace('\t',' ');
            str = str.replace('\n',' ');
            str = str.trim();
            arr[i] = str;
        }
    }

    @Override
    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) req;
        Enumeration requestParamNames = req.getParameterNames();
        boolean isMatch = false;
        String paramName, detectValue = null;
        String requestUri = ((HttpServletRequest) req).getRequestURI();
        String contextPath = ((HttpServletRequest) req).getContextPath();
        requestUri = requestUri.substring(contextPath.length());
        String[] paramValues;
        HttpServletResponse response = (HttpServletResponse) resp;

        for (String uriSuffix : encodeFiltPathArr) {
            if (uriSuffix.equals(requestUri)) {
                chain.doFilter(new XssHttpServletRequestWrapper((HttpServletRequest) request, charset), response);
                return;
            }
        }

        while(requestParamNames.hasMoreElements()){
            paramName = (String)requestParamNames.nextElement();

            /**
             * 在配置中指定的uri对应参数不应该被过滤,如登录密码不应该被过滤
             * */
            if (filterLegalUriParamNames(requestUri, paramName)) {
                continue;
            }

            paramValues = request.getParameterValues(paramName);
            if (paramValues == null)  {
                continue;
            }

            for (int i = 0; i < paramValues.length; i++) {
                detectValue = paramValues[i];
                isMatch = filterInvalidParam(detectValue);
                if (isMatch) {
                    break;
                }
            }

            if ( isMatch ){
                logger.error("\r\n param :"+ paramName +" contains invalid value: \"" + detectValue + "\" \r\n ip:"+request.getRemoteHost());
                return ;
            }
        }
        chain.doFilter(request, response);
    }

    /**
     * 指定的uri和paramName是否无需被过滤
     * @param uri
     * @param paramName
     * @return      boolean (true:无需被过滤,false:应被过滤)
     * @date        2018/10/19 10:29
     */
    private boolean filterLegalUriParamNames(String uri, String paramName) {
        if (legalUriParamNameArr == null || legalUriParamNameArr.length == 0) {
            return false;
        }

        String[] uriAndParamNames;
        String legalUri,legalParamName;

        for (String legalUriWithParamName : legalUriParamNameArr) {
            uriAndParamNames = legalUriWithParamName.split(LEGAL_URI_WITH_PARAMNAMES_SEPERATOR);
            if (uriAndParamNames.length < 2 ) {
                continue;
            }

            legalUri = uriAndParamNames[0];
            if (!legalUri.equals(uri)) {
                continue;
            }

            for (int i = 1; i < uriAndParamNames.length; i++) {
                legalParamName = uriAndParamNames[i];
                if (legalParamName.equals(paramName)) {
                    return true;
                }
            }
        }

        return false;
    }

    private boolean filterInvalidParam(String paramValue) {
        if (StringUtils.isEmpty(paramValue)) {
            return false;
        }

        if (invalidParamArr == null || invalidParamArr.length == 0) {
            return false;
        }

        for (String invalidParam : invalidParamArr) {
            if (paramValue.toUpperCase().contains(invalidParam.toUpperCase())) {
                return true;
            }
        }
        return false;
    }

    @Override
    public void destroy() {

    }
}
